package mahjong

import (
	"Mahjong-demo/card"
	"container/list"
	"fmt"
	"math"
	"sort"
	"strconv"
)

type mahjongList struct {
	// card.List
	// 每种花色牌的起始坐标, 没有用 nil 表示
	// suitPos []*list.Element
	data []*list.List
}

// NewList 麻将列表
func newList(cardIDs []int) *mahjongList {
	// 整理牌
	sort.Ints(cardIDs)
	data := make([]*list.List, card.SuitSize, card.SuitSize)
	for n := 0; n < card.SuitSize; n++ {
		data[n] = list.New()
	}

	var lst *list.List
	for _, v := range cardIDs {
		lst = data[card.GetSuit(v)]
		lst.PushBack(v)
	}

	return &mahjongList{data}
}

func (c *mahjongList) add(cardID int) {
	var suit = card.GetSuit(cardID)
	if suit >= card.SuitSize {
		return
	}

	for e := c.data[suit].Front(); e != nil; e = e.Next() {
		if e.Value.(int) > cardID {
			c.data[suit].InsertBefore(cardID, e)
		}
	}
}

func (c *mahjongList) drop(cardID int) bool {
	e := c.find(cardID)
	if e == nil {
		return false
	}

	c.data[card.GetSuit(cardID)].Remove(e)
	return true
}

func (c *mahjongList) find(cardID int) *list.Element {
	var suit = card.GetSuit(cardID)
	if suit >= card.SuitSize {
		return nil
	}

	for e := c.data[suit].Front(); e != nil; e = e.Next() {
		if e.Value.(int) == cardID {
			return e
		}
	}

	return nil
}

// Swap 替换指定牌，如：摸牌后出牌
func (c *mahjongList) swap(idDrop int, idAdd int) {
	if idDrop == idAdd {
		return
	}

	if c.drop(idDrop) {
		c.add(idAdd)
	}
}

// Do 遍历对每个元素执行函数f
func (c *mahjongList) do(f func(cardID int)) {
	for _, lst := range c.data {
		for e := lst.Front(); e != nil; e = e.Next() {
			f(e.Value.(int))
		}
	}
}

func (c *mahjongList) chi(cardID int) []int {
	var suit = card.GetSuit(cardID)
	if suit >= card.SuitSize {
		return nil
	}

	var diff int
	var guessLen = 0
	var ignorePos = 0
	cardDetail := card.GetCardByID(cardID)
	guess := make([]int, catchHandCard)
	dropPos := []int{0, 0}
	for e := c.data[suit].Front(); e != nil; e = e.Next() {
		if cardDetail.Suit != card.GetCardByID(e.Value.(int)).Suit {
			break
		}

		diff = e.Value.(int) - cardID
		absDiff := int(math.Abs(float64(diff)))
		if absDiff == 1 || absDiff == 2 {
			// fmt.Printf("add guess %v\n", e.Value.(int))

			if guessLen > 0 && (int(math.Abs(float64(e.Value.(int)-guess[guessLen-1]))) > 2 ||
				(int(math.Abs(float64(e.Value.(int)-guess[guessLen-1]))) == 2 &&
					int(math.Abs(float64(guess[guessLen-1]-cardID))) != 1)) {
				// fmt.Printf("放弃掉: %v, %v\n", guess[:guessLen], guessLen)
				dropPos[ignorePos] = guessLen
			}
			guess[guessLen] = e.Value.(int)
			guessLen++
		} else if guessLen != 0 && absDiff != 0 {
			break
		}
	}

	if dropPos[1] != 0 {
		guessLen = dropPos[1]
	}

	result := guess[dropPos[0]:guessLen]
	fmt.Printf("reuslt: %v\n", result)
	if len(result) >= 2 {
		return result
	}

	return nil
}

func (c *mahjongList) peng(cardID int) bool {
	e := c.find(cardID)
	if e == nil {
		return false
	}

	eNext := e.Next()
	if eNext == nil || eNext.Value.(int) != cardID {
		return false
	}

	return true
}

func (c *mahjongList) gang(cardID int) bool {
	e := c.find(cardID)
	if e == nil {
		return false
	}

	eNext := e.Next()
	for n := 0; n < 2; n++ {
		if eNext == nil || eNext.Value.(int) != cardID {
			return false
		}
		eNext = eNext.Next()
	}

	return true
}

func isKeZi(e1, e2, e3 *list.Element) bool {
	return e1.Value.(int) == e2.Value.(int) && e1.Value.(int) == e3.Value.(int)
}

func isShunZi(e1, e2, e3 *list.Element) bool {
	return e1.Value.(int)+1 == e2.Value.(int) &&
		e1.Value.(int)+2 == e3.Value.(int)
}

func isDuiZi(e1, e2 *list.Element) bool {
	return e1.Value.(int) == e2.Value.(int)
}

// 补全顺子, 只能补一张
func completeShunzi2(e1, e2 *list.Element) *list.List {
	if e1 == nil || e2 == nil {
		return nil
	}

	diff := math.Abs(float64(e1.Value.(int)) - float64(e2.Value.(int)))
	if diff > 2 {
		// fmt.Println("不能听牌(不能组成顺子):", card.GetFriendy(e1.Value.(int)), card.GetFriendy(e2.Value.(int)))
		return nil
	}

	if diff == 0 {
		// fmt.Println("程序内部错误~", e1.Value.(int), e2.Value.(int))
		return nil
	}

	// 花色检查
	if card.GetSuit(e1.Value.(int)) != card.GetSuit(e1.Value.(int)) {
		// fmt.Println("不能听牌(不能组成顺子,花色不同):", card.GetFriendy(e1.Value.(int)), card.GetFriendy(e2.Value.(int)))
		return nil
	}

	ret := list.New()
	if diff == 2 {
		ret.PushBack(e1.Value.(int) + 1)
	} else {
		// diff == 1
		if card.GetSuit(e1.Value.(int)-1) == card.GetSuit(e1.Value.(int)) {
			ret.PushBack(e1.Value.(int) - 1)
		}

		if card.GetSuit(e2.Value.(int)+1) == card.GetSuit(e1.Value.(int)) {
			ret.PushBack(e2.Value.(int) + 1)
		}
		if ret.Len() != 2 {
			return nil
		}
	}

	// fmt.Println("ret:", ret)

	return ret
}

// 验证是否能够听牌 (成都)血战麻将
// 返回听牌(可以用作和牌的牌)
func (c *mahjongList) ting() *list.List {
	// 检查列表中花色数量
	var suitCount = 0
	for _, lst := range c.data {
		if lst.Len() > 0 {
			suitCount++
		}
	}

	// 三种花色, 未达到缺门条件
	if suitCount == 3 {
		fmt.Println("没有缺门")
		return nil
	}

	var finished = false
	unDuiZi := list.New()
	var lst []*list.List
	// 保存非对子牌和所有牌, 列表长度为对子数+1(所有牌)
	markDuiZi := make(map[*list.Element]interface{})
	for !finished {
		lst, finished = c.getUncompleteDuiZi(markDuiZi)
		// fmt.Println("map len:", len(markDuiZi))
		unDuiZi.PushBack(lst)
	}

	var duiziCount = unDuiZi.Len() - 1
	// fmt.Println("猜解对子:", duiziCount)

	// 检测特殊牌型`七对`
	if duiziCount == 6 {
		fmt.Println("七对, 听=>")
		for _, suit := range c.data {
			for e := suit.Front(); e != nil; {
				if e.Next() != nil && isDuiZi(e, e.Next()) {
					e = e.Next().Next()
				} else {
					lst := list.New()
					lst.PushBack(e.Value.(int))
					return lst
				}
			}
		}
		return nil
	}

	// 唯一过滤， 字符串的key比较耗时
	unique := make(map[string]interface{})
	procRemain := map[int]func(lst *list.List, unique map[string]interface{}) bool{
		1: remain1,
		2: remain2,
		4: remain4,
	}

	var lst1, lst2, t1, tmp, final, ret *list.List
	for re := unDuiZi.Front(); re != nil; re = re.Next() {
		for _, suit := range re.Value.([]*list.List) {
			// 不可组成刻子的牌
			lst1 = completeKeZi(suit)
			// 不可组成顺子的牌
			lst2 = completeShunZi(suit)

			if lst1.Len() > 1 {
				t1 = completeShunZi(lst1)
				for e := t1.Front(); e != nil; e = e.Next() {
					final = e.Value.(*list.List)

					proc, ok := procRemain[final.Len()]
					if !ok {
						continue
					}

					proc(final, unique)
				}
			} else if lst1.Len() == 1 {
				remain1(lst1, unique)
			}

			for e := lst2.Front(); e != nil; e = e.Next() {
				tmp = e.Value.(*list.List)
				if tmp.Len() > 1 {
					final = completeKeZi(tmp)
					proc, ok := procRemain[final.Len()]
					if !ok {
						continue
					}

					proc(final, unique)
				} else if tmp.Len() == 1 {
					remain1(tmp, unique)
				}
			}
		}
	}

	// 单吊`将牌`数
	var tingJiang = 0
	ret = list.New()
	for k := range unique {
		for n := 0; n < len(k)/2; n++ {
			v, err := strconv.ParseInt(k[n*2 : (n+1)*2], 16, 0)
			if err != nil {
				continue
			}
			ret.PushBack(int(v))
		}
		if len(k) == 2 {
			tingJiang++
		}
		ret.PushBack(-1)
	}

	// 既无将牌, 也没有单吊将牌则不能听牌
	if duiziCount == 0 && tingJiang == 0 {
		return nil
	}

	return ret
}

func (c *mahjongList) getUncompleteDuiZi(mark map[*list.Element]interface{}) ([]*list.List, bool) {
	// 所有不能组合成对子的牌
	uncompleteDuiZi := make([]*list.List, card.SuitSize)
	for n := 0; n < card.SuitSize; n++ {
		uncompleteDuiZi[n] = list.New()
	}

	var oldLen = len(mark)
	for n, suit := range c.data {
		lst := list.New()
		for e := suit.Front(); e != nil; {
			_, ok := mark[e]
			if oldLen == len(mark) && !ok && e.Next() != nil && e.Value.(int) == e.Next().Value.(int) {
				mark[e] = nil
				mark[e.Next()] = nil
				// fmt.Println("将:", card.GetFriendy(e.Value.(int)))
				e = e.Next().Next()
				continue
			}
			// fmt.Println(e.Value.(int))
			lst.PushBack(e.Value.(int))
			e = e.Next()
		}

		uncompleteDuiZi[n] = lst
	}

	// fmt.Println("Debug: ", uncompleteDuiZi[0])
	// fmt.Println("Debug: ", uncompleteDuiZi[1])
	// fmt.Println("Debug: ", uncompleteDuiZi[2])
	return uncompleteDuiZi, oldLen == len(mark)
}

// 组合顺子, 返回不可组合的牌
func completeShunZi(lst *list.List) *list.List {
	ret := list.New()
	tmp := list.New()
	for n := 0; n < lst.Len(); n++ {
		result := list.New()
		result.PushBackList(lst)

		offset := result.Front()
		for i := 0; i < n && offset != nil; i++ {
			tmp.PushBack(offset.Value.(int))
			result.Remove(offset)
			offset = result.Front()
		}

		// fmt.Println("tmp: ", tmp.Len(), getCardsName2(tmp))

		for removeShunZi(result) {
		}

		for e := result.Front(); tmp.Len() > 0 && e != nil; e = e.Next() {
			// fmt.Println(e, tmp.Front())
			if e.Value.(int) > tmp.Front().Value.(int) {
				for tmp.Len() > 0 {
					result.InsertBefore(tmp.Front().Value.(int), e)
					tmp.Remove(tmp.Front())
				}
			}
		}
		result.PushBackList(tmp)
		for tmp.Len() > 0 {
			tmp.Remove(tmp.Front())
		}

		for removeShunZi(result) {
		}

		// fmt.Println("zking: ", getCardsName2(result))
		ret.PushBack(result)
	}

	return ret
}

func removeShunZi(lst *list.List) bool {
	// 组合顺子
	for e1 := lst.Front(); e1 != nil; e1 = e1.Next() {
		for e2 := e1.Next(); e2 != nil; e2 = e2.Next() {
			for e3 := e2.Next(); e3 != nil; e3 = e3.Next() {
				if isShunZi(e1, e2, e3) {
					lst.Remove(e1)
					lst.Remove(e2)
					lst.Remove(e3)
					return true
				}
			}
		}
	}

	return false
}

func remain2(lst *list.List, unique map[string]interface{}) bool {
	if isDuiZi(lst.Front(), lst.Back()) ||
		completeShunzi2(lst.Front(), lst.Back()) != nil {
		unique[fmt.Sprintf("%.2x%.2x", lst.Front().Value.(int), lst.Back().Value.(int))] = nil
		return true
	}

	return false
}

func remain1(lst *list.List, unique map[string]interface{}) bool {
	unique[fmt.Sprintf("%.2x", lst.Front().Value.(int))] = nil
	return true
}

func remain4(lst *list.List, unique map[string]interface{}) bool {
	removeShunZi(lst)
	if lst.Len() == 1 {
		remain1(lst, unique)
		return true
	}

	duizi := list.New()
	others := list.New()
	for e := lst.Front(); e != nil; {
		if e.Next() != nil && isDuiZi(e, e.Next()) {
			duizi.PushBack(e.Value.(int))
			e = e.Next().Next()
			continue
		}
		// 保存非`对子`牌
		others.PushBack(e.Value.(int))
		e = e.Next()
	}

	// 剩余的是2对
	if duizi.Len() == 2 {
		v1 := duizi.Front().Value.(int)
		v2 := duizi.Back().Value.(int)
		unique[fmt.Sprintf("%.2x%.2x%.2x%.2x", v1, v1, v2, v2)] = nil
		return true
	}

	if duizi.Len() == 1 {
		return remain2(others, unique)
	}

	return false
}

func getDuiZiCount(lst *list.List) int {
	var count = 0
	for e := lst.Front(); e != nil; {
		if e.Next() != nil && isDuiZi(e, e.Next()) {
			count++
			e = e.Next().Next()
		}

		e = e.Next()
	}

	return count
}

// 组合刻子, 返回不可组合的牌
func completeKeZi(lst *list.List) *list.List {
	ret := list.New()
	for e := lst.Front(); e != nil; {
		if e.Next() != nil && e.Next().Next() != nil && isKeZi(e, e.Next(), e.Next().Next()) {
			e = e.Next().Next().Next()
		} else {
			ret.PushBack(e.Value.(int))
			e = e.Next()
		}
	}

	return ret
}

func getCardsName2(lst *list.List) string {
	str := "["
	for e := lst.Front(); e != nil; e = e.Next() {
		if e.Value.(int) == -1 {
			str += "], ["
			continue
		}
		str += card.GetFriendy(e.Value.(int)) + ","
	}
	str += "]"

	return str
}
